package ad.media 
{
	import ad.events.MediaEvent;
	import ad.events.EventDispatcherExpert;
	
	import flash.events.EventDispatcher;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.events.TimerEvent;
	import flash.media.ID3Info;
	import flash.media.Sound;
	import flash.media.SoundChannel;
	import flash.media.SoundLoaderContext;
	import flash.media.SoundMixer;
	import flash.net.URLRequest;
	import flash.utils.Timer;
	
	[Event(name = 'ioError', type = 'flash.events.IOErrorEvent')]
	[Event(name = 'securityError', type = 'flash.events.SecurityErrorEvent')]
	[Event(name = 'open', type = 'flash.events.Event')]
	[Event(name = 'id3', type = 'flash.events.Event')]
	[Event(name = 'progress', type = 'flash.events.ProgressEvent')]
	[Event(name = 'playProgress', type = 'flash.events.ProgressEvent')]
	[Event(name = 'complete', type = 'flash.events.Event')]
	[Event(name = 'soundComplete', type = 'flash.events.Event')]
	
	/**
	 * SoundFacade
	 * @version ActionScript 3.0
	 * @author Adrian C. Miranda
	 * @mail ad@adrianmiranda.com.br
	 */
	public class SoundFacade extends EventDispatcher
	{	
		public var sound:Sound;
		public var soundChannel:SoundChannel;
		
		public var url:String;
		public var autoLoad:Boolean = false;
		public var autoPlay:Boolean = false;
		public var isStreaming:Boolean = false;
		public var bufferTime:int = 1000;
		
		public var isLoaded:Boolean = false;
		public var isPlaying:Boolean = false;
		public var isReadyToPlay:Boolean = false;
		
		public var pausePosition:int = 0;
		
		public static const PLAY_PROGRESS:String = 'playProgress';
		public var progressInterval:int = 10;
		public var timer:Timer;
		public var presentTime:String;
		public var totalTime:String;
		
		public function SoundFacade(url:String, autoLoad:Boolean = true, autoPlay:Boolean = true, streaming:Boolean = true, bufferTime:int = -1)
		{
			this.url = url;
			this.autoLoad = autoLoad;
			this.autoPlay = autoPlay;
			this.isStreaming = streaming;
			if (bufferTime < 0) bufferTime = SoundMixer.bufferTime;
			this.bufferTime = Math.min(Math.max(0, bufferTime), 30000);
			if (autoLoad) load();
		}
		
		public function get id3():ID3Info { return sound.id3; }
		public function get position():Number { return Math.ceil(soundChannel.position); }
		public function get length():Number { return Math.ceil(sound.length / (sound.bytesLoaded / sound.bytesTotal)); }
		
		public function load():void
		{
			if (isPlaying)
			{
				stop();
				sound.close();
			}
			isLoaded = false;
			sound = new Sound();
			sound.addEventListener(IOErrorEvent.IO_ERROR, onSoundIOError, false, 0, true);
			sound.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSoundSecurityError, false, 0, true);
			sound.addEventListener(Event.OPEN, onSoundOpen, false, 0, true);
			sound.addEventListener(Event.ID3, onSoundID3, false, 0, true);
			sound.addEventListener(ProgressEvent.PROGRESS, onSoundProgress, false, 0, true);
			sound.addEventListener(Event.COMPLETE, onSoundComplete, false, 0, true);
			var stream:URLRequest = new URLRequest(url);
			var soundLoaderContext:SoundLoaderContext = new SoundLoaderContext(bufferTime, true);
			sound.load(stream, soundLoaderContext);
		}
		
		public function play(position:int = 0):void
		{
			if (!isPlaying)
			{
				if (isReadyToPlay)
				{
					try 
					{
						soundChannel = sound.play(position);
						soundChannel.addEventListener(Event.SOUND_COMPLETE, onSoundChannelComplete, false, 0, true);
						isPlaying = true;
						timer = new Timer(this.progressInterval); 
						timer.addEventListener(TimerEvent.TIMER, onSoundTimer, false, 0, true); 
						timer.start();
					}
					catch (error:Error)
					{
						trace(this, error.message);
					}
				}
			}
		}
		
		public function pause():void
		{
			stop(soundChannel.position);
		}
		
		public function resume():void
		{
			play(pausePosition);
		}
		
		public function stop(position:int = 0):void
		{
			if (isPlaying)
			{
				pausePosition = position;
				soundChannel.stop();
				timer.stop();
				isPlaying = false;
			}
		}
		
		private function onSoundSecurityError(evt:SecurityErrorEvent):void
		{
			dispatchEvent(evt.clone());
		}
		
		private function onSoundIOError(evt:IOErrorEvent):void
		{
			dispatchEvent(evt.clone());
		}
		
		private function onSoundOpen(evt:Event):void
		{
			if (isStreaming)
			{
				isReadyToPlay = true;
				if (autoPlay) play();
			}
			dispatchEvent(evt.clone());
		}
		
		private function onSoundID3(evt:Event):void
		{
			dispatchEvent(evt.clone());
		}
		
		private function onSoundProgress(evt:ProgressEvent):void
		{
			dispatchEvent(evt.clone());
		}
		
		private function onSoundComplete(evt:Event):void
		{
			isReadyToPlay = true;
			isLoaded = true;
			dispatchEvent(evt.clone());
			if (autoPlay && !isPlaying) play();
		}
		
		private function onSoundTimer(evt:TimerEvent):void
		{
			calculatingTime();
			dispatchEvent(new ProgressEvent(SoundFacade.PLAY_PROGRESS, false, false, position, length));
		}
		
		private function onSoundChannelComplete(evt:Event):void
		{
			isPlaying = false;
			timer.stop();
			timer.dispatchEvent(new TimerEvent(TimerEvent.TIMER));
			timer.removeEventListener(TimerEvent.TIMER, onSoundTimer);
			timer = null;
			dispatchEvent(evt.clone());
		}
		
		private function calculatingTime():void 
		{
			var time:Number = length * 0.001;
			var currentmin:int = int((position * 0.001) / 60);
			var currentseg:int = int((position * 0.001) % 60);
			var totalmin:int = int(time / 60);
			var totalseg:int = int(time % 60);
			presentTime = String(timeFormat(currentmin) + ":" + timeFormat(currentseg));
			totalTime = String(timeFormat(totalmin) + ":" + timeFormat(totalseg));
			//trace(presentTime, '/', totalTime);
		}
		
		private function timeFormat(value:int):String
		{
			return String(value < 10 ? "0" + value : value);
		}
		
	}

}